using System;
using System.Collections;
using System.Runtime.Serialization;
using System.Security.Permissions;

namespace Meta
{
	[Serializable]
	public abstract class MetaException : Exception {
		public MetaException() : base () { }
		public MetaException(string message) : base (message) { }
		public MetaException(SerializationInfo info, StreamingContext context) : base(info, context) {}
	}
	[Serializable]
	public class ArgumentCountException : ArgumentException, ISerializable {
		public ArgumentCountException(object[] argumentsProvidedArg, object[] expectedArgumentsArg)  {
			argumentsProvided=argumentsProvidedArg;
			expectedArguments=expectedArgumentsArg;
		}

		public ArgumentCountException(SerializationInfo info, StreamingContext context) : base(info, context) {
			argumentsProvided = (object[])info.GetValue("argumentsProvided", typeof(object[]));
			expectedArguments = (object[])info.GetValue("expectedArguments", typeof(object[]));
		}

		[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
		public override void GetObjectData(SerializationInfo info, StreamingContext context) {
			base.GetObjectData(info, context);
			info.AddValue("argumentsProvided", RemoteDataPlaceholder.PlaceholderArray(argumentsProvided));
			info.AddValue("expectedArguments", RemoteDataPlaceholder.PlaceholderArray(expectedArguments));
		}

		public object[] argumentsProvided;
		public object[] expectedArguments;
		public override String Message {

			get {
                if (argumentsProvided == null || expectedArguments == null)
                    return "Wrong number of arguments to procedure";
				return "Wrong number of arguments to procedure - expected "+(((String)(expectedArguments.Length.ToString()))+(" got "+((String)(argumentsProvided.Length.ToString()))));
			}
		}
	}

	[Serializable]
	public class ArgumentTypeException : ArgumentException, ISerializable {
		public ArgumentTypeException(object argumentNameArg, object valueProvidedArg, Type expectedTypeArg)  {
			argumentName=argumentNameArg;
			valueProvided=valueProvidedArg;
			expectedType=expectedTypeArg;
		}
		public ArgumentTypeException(SerializationInfo info, StreamingContext context) : base(info, context) {
			argumentName = info.GetValue("argumentName", typeof(object));
			valueProvided = info.GetValue("valueProvided", typeof(object));
			expectedType = info.GetValue("expectedType", typeof(object));
		}

		[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
		public override void GetObjectData(SerializationInfo info, StreamingContext context) {
			base.GetObjectData(info, context);
			info.AddValue("argumentName", argumentName);
			info.AddValue("expectedType", new RemoteDataPlaceholder(expectedType));
			info.AddValue("valueProvided", new RemoteDataPlaceholder(valueProvided));
		}

		private object argumentName;
		public object expectedType;
		public object valueProvided;
		public override String ParamName {

			get {
				return ((String)(argumentName.ToString()));
			}
		}
		public override String Message {

			get {
				return "The value passed for the "+(ParamName+(" argument was not a "+((expectedType is Type)?((Type)expectedType).Name:expectedType)));
			}
		}
	}

	[Serializable]
	public class ArithmeticTypeException : ArgumentTypeException {
		public ArithmeticTypeException(object argument, object val) : base (argument, val, PseudoType.Arithmetic) { }
		public ArithmeticTypeException(SerializationInfo info, StreamingContext context) : base(info, context) { }
	}

	[Serializable]
	public class TypeException : ArgumentException, ISerializable {
		public TypeException(String m_messageArg, object valueProvidedArg, Type expectedTypeArg)  {
			m_message=m_messageArg;
			valueProvided=valueProvidedArg;
			expectedType=expectedTypeArg;
		}

		public TypeException(SerializationInfo info, StreamingContext context) : base(info, context) {
			valueProvided = info.GetValue("valueProvided", typeof(object));
			expectedType = info.GetValue("expectedType", typeof(object));
		}

		[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
		public override void GetObjectData(SerializationInfo info, StreamingContext context) {
			base.GetObjectData(info, context);
			info.AddValue("expectedType", new RemoteDataPlaceholder(expectedType));
			info.AddValue("valueProvided", new RemoteDataPlaceholder(valueProvided));
		}

		private String m_message;
		public object expectedType;
		public object valueProvided;
		public override String Message {

			get {
				return m_message;
			}
		}
	}

	[Serializable]
	public class TestTypeException : TypeException {
		public TestTypeException(string message, object value) : base (message, value, typeof(bool)) { }
		public TestTypeException(SerializationInfo info, StreamingContext context) : base(info, context) { }
	}
	
	[Serializable]
	public class SyntaxError : MetaException, ISerializable {
		public SyntaxError() {}
		public SyntaxError(String messageArg, object offendingExpressionArg)  {
			message=messageArg;
			offendingExpression=offendingExpressionArg;
			SurroundingExpressions=null;
			SurroundingExpressions = ((System.Collections.Stack)(Reader.readerStack.Value)).ToArray();
		}

		public SyntaxError(SerializationInfo info, StreamingContext context) : base(info, context) {
			message = (string)info.GetValue("message", typeof(string));
			offendingExpression = info.GetValue("offendingExpression", typeof(object));
			SurroundingExpressions = (object[])info.GetValue("SurroundingExpressions", typeof(object[]));
		}

		[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
		public override void GetObjectData(SerializationInfo info, StreamingContext context) {
			base.GetObjectData(info, context);
			info.AddValue("message", message);
			info.AddValue("offendingExpression", offendingExpression);
			info.AddValue("SurroundingExpressions", RemoteDataPlaceholder.PlaceholderArray(SurroundingExpressions));
		}

		protected String message;
		public object offendingExpression;
		public object[] SurroundingExpressions;
		public override String Message {

			get {
				return message;
			}
		}
	}
	[Serializable]
	public class LexicalSyntaxError : SyntaxError {
		public LexicalSyntaxError() {}
		public LexicalSyntaxError(String messageArg, object offendingExpressionArg)  {
			message=messageArg;
			offendingExpression=offendingExpressionArg;
		}

		protected LexicalSyntaxError(SerializationInfo info, StreamingContext context)  : base(info, context) { }
	}

	[Serializable]
	public class ParseError : SyntaxError, ISerializable {
		public ParseError() { }
		public ParseError(String messageArg, object offendingExpressionArg) : base(messageArg, offendingExpressionArg)  {
		}

		//void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
		//	base.GetObjectData(info, context);
		//}

		protected ParseError(SerializationInfo info, StreamingContext context)  : base(info, context) { }
	}

	[Serializable]
	public class NumericSyntaxError : LexicalSyntaxError {
		public NumericSyntaxError(String MsgArg, object ExpArg)  : base(MsgArg, ExpArg)  {
		}
		protected NumericSyntaxError(SerializationInfo info, StreamingContext context)  : base(info, context) { }
	}

	[Serializable]
	public class IncompleteExpressionError : LexicalSyntaxError {
		public IncompleteExpressionError(String MsgArg, object ExpArg)  : base(MsgArg, ExpArg)  {
		}
		protected IncompleteExpressionError(SerializationInfo info, StreamingContext context)  : base(info, context) { }
	}

	[Serializable]
	public class ExtraBracketError : LexicalSyntaxError {
		public ExtraBracketError(String MsgArg, object ExpArg)  : base(MsgArg, ExpArg)  {
		}
		protected ExtraBracketError(SerializationInfo info, StreamingContext context)  : base(info, context) { }
	}

	[Serializable]
	public class MissingBodyError : ParseError {
		public MissingBodyError(String MsgArg, object ExpArg)  : base(MsgArg, ExpArg)  {
		}
		protected MissingBodyError(SerializationInfo info, StreamingContext context)  : base(info, context) { }
	}

	[Serializable]
	public class ProcedureSyntaxError : ParseError {
		public ProcedureSyntaxError(String MsgArg, object ExpArg)  : base(MsgArg, ExpArg)  {
		}
		protected ProcedureSyntaxError(SerializationInfo info, StreamingContext context)  : base(info, context) { }
	}

	[Serializable]
	public class UnnecessaryCodeError : ParseError {
		public UnnecessaryCodeError(Expression e, IList surrounding) {
			offendingExpression = e;
			SurroundingExpressions = SequenceUtils.AsObjectArray(surrounding);
		}
		public override string Message {
			get {
				return String.Format("The expression {0} was used in a context where it could not affect the result of the computation",
					Writer.SafeWriteToString(((Expression)offendingExpression).Unparse(), 50));
			}
		}

		protected UnnecessaryCodeError(SerializationInfo info, StreamingContext context) : base(info, context) { }
	}

	[Serializable]
	public class EmptyBodyError : ParseError {
		public EmptyBodyError() {
		}

		public override string Message {
			get {
				return "No return value expression given";
			}
		}
		protected EmptyBodyError(SerializationInfo info, StreamingContext context)  : base(info, context) { }
	}

	[Serializable]
	public class InvalidArgumentDeclarationError : ParseError {
		public InvalidArgumentDeclarationError(object objectInQuestionArg)  {
			offendingExpression=objectInQuestionArg;
		}
		public override String Message {

			get {
				return "Procedure declaration contains a malformed parameter name";
			}
		}

		protected InvalidArgumentDeclarationError(SerializationInfo info, StreamingContext context)  : base(info, context) { }
	}

	[Serializable]
	public class InvalidTypeExpressionError : ParseError {
		public InvalidTypeExpressionError(object o) {
			offendingExpression = o;
		}
		public override String Message {
			get {
				return "The expression for the variable's type is not a valid type name";
			}
		}

		protected InvalidTypeExpressionError(SerializationInfo info, StreamingContext context)  : base(info, context) { }
	}

	[Serializable]
	public class MalformedDefinitionError : ParseError {
		public MalformedDefinitionError(string msg, object o) {
			offendingExpression = o;
			message = msg;
		}
		protected MalformedDefinitionError(SerializationInfo info, StreamingContext context)  : base(info, context) { }
	}

	[Serializable]
	public class MalformedDefinitionVariableError : MalformedDefinitionError, ISerializable {
		public object variableName;
		public MalformedDefinitionVariableError(string msg, object o, object var) : base(msg, o) {
			variableName = var;
		}

		[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
		public override void GetObjectData(SerializationInfo info, StreamingContext context) {
			base.GetObjectData(info, context);
			info.AddValue("variableName", variableName);
		}

		protected MalformedDefinitionVariableError(SerializationInfo info, StreamingContext context)  : base(info, context) {
			variableName = info.GetValue("variableName", typeof(object));
		}
	}

	[Serializable]
	public class CheckFailed : Exception, ISerializable {
		public CheckFailed(Procedure testArg, object valueArg)  {
			test=testArg;
			failedValue=valueArg;
		}
		public Procedure test;
		public object failedValue;
		public override String Message {

			get {
				return "The test valued of the value";
			}
		}

		[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
		public override void GetObjectData(SerializationInfo info, StreamingContext context) {
			info.AddValue("test", test);
			info.AddValue("failedValue", new RemoteDataPlaceholder(failedValue));
		}

		protected CheckFailed(SerializationInfo info, StreamingContext context) {
			test = (Procedure)info.GetValue("test", typeof(Procedure));
			failedValue = info.GetValue("failedValue", typeof(object));
		}

		
	}

	[Serializable]
	public class UndefinedVariableException : MetaException, ISerializable {
		public UndefinedVariableException(GlobalVariable variableArg)  {
			variable=variableArg;
		}
		public GlobalVariable variable;
		public override String Message {

			get {
				return "Attempt to use a global variable, \""+(variable.name.name+"\", that has not been given a value.");
			}
		}

		public override void GetObjectData(SerializationInfo info, StreamingContext context) {
			info.AddValue("variable", variable);
		}

		protected UndefinedVariableException(SerializationInfo info, StreamingContext context) {
			variable = (GlobalVariable)info.GetValue("variable", typeof(GlobalVariable));
		}
	}

	[Serializable]
	public class BadProcedureException : MetaException, ISerializable {
		public BadProcedureException(object offendingProcedureArg)  {
			offendingProcedure=offendingProcedureArg;
		}
		public object offendingProcedure;
		public override String Message {

			get {
				return "Attempt to call something that wasn't a procedure";
			}
		}

		[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
		public override void GetObjectData(SerializationInfo info, StreamingContext context) {
			info.AddValue("offendingProcedure", new RemoteDataPlaceholder(offendingProcedure));
		}

		protected BadProcedureException(SerializationInfo info, StreamingContext context) {
			offendingProcedure = (Procedure)info.GetValue("offendingProcedure", typeof(object));
		}
	}

	[Serializable]
	public class UnknownMemberException : MetaException, ISerializable {
		public UnknownMemberException(object objectInQuestionArg, String memberInQuestionArg)  {
			objectInQuestion=objectInQuestionArg;
			memberInQuestion=memberInQuestionArg;
		}
		public object objectInQuestion;
		public String memberInQuestion;

		[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
		public override void GetObjectData(SerializationInfo info, StreamingContext context) {
			info.AddValue("objectInQuestion", new RemoteDataPlaceholder(objectInQuestion));
			info.AddValue("memberInQuestion", memberInQuestion);
		}

		protected UnknownMemberException(SerializationInfo info, StreamingContext context) {
			objectInQuestion = info.GetValue("objectInQuestion", typeof(RemoteDataPlaceholder));
			memberInQuestion = (string)info.GetValue("memberInQuestion", typeof(string));
		}

		public override String Message {

			get {
				return "Program tried to use a non-existent member named "+memberInQuestion;
			}
		}
	}


	[Serializable]
	public class Warning : System.IComparable {
		public Warning(Expression e, Expression site, string detail) {
			expression = e;
			this.site = site;
			this.detail = detail;
		}
		public Expression expression;
		public Expression site;
		public string detail;

		public static int Position(Expression e, Expression site, IList warnings) {
			for (int i=0; i<warnings.Count; i++) {
				Warning w = (Warning)warnings[i];
				if (w.expression==e && w.site==site)
					return i;
			}
			return -1;
		}

		int IComparable.CompareTo(object o) {
			if (this==o)
				return 0;

			Warning w = o as Warning;
			if (w==null)
				return 1;
			if (detail==null) {
				if (w.detail==null)
					return 0;
				else
					return -1;
			} else if (w.detail==null)
				return 1;
			else
				return String.Compare(this.detail, w.detail);
		}
	}

	[Serializable]
	public class MismatchedListArgumentException : MetaException {
		public MismatchedListArgumentException() : base () { }
		public MismatchedListArgumentException(string message) : base (message) { }
		public MismatchedListArgumentException(SerializationInfo info, StreamingContext context) : base(info, context) {}

	}

	[Serializable]
	public class EmptyListArgumentException : MetaException {
		public EmptyListArgumentException() : base () { }
		public EmptyListArgumentException(string message) : base (message) { }
		public EmptyListArgumentException(SerializationInfo info, StreamingContext context) : base(info, context) {}

	}

}
